#include "serialport.h"
#include <QDebug>
#include <QDateTime>

SerialPort::SerialPort(QObject *parent)
    : BasePort (parent)
{
    m_port = new QSerialPort(this);
}

SerialPort::~SerialPort()
{
    if (m_port->isOpen())
    {
        m_port->close();
    }
    delete m_port;
}

BasePort::PortType SerialPort::getPortType()
{
    return PortType::PortType_Serial;
}

void SerialPort::setConfig(QVariant &config)
{
    QMutexLocker locker(m_mutex);
    m_setting = config.value<SerialPortSetting>();
}

QVariant SerialPort::getConfig(void)
{
    QMutexLocker locker(m_mutex);
    QVariant var;
    var.setValue(m_setting);
    return var;
}

// windows和linux跨线程打开串口会报警
// 因为当前线程为ui线程，open()时会new一个将QSerialPort做父类的对象，而QSerialPort类在port_manager_thread线程
// 跨线程请调用openSync()
bool SerialPort::open(void)
{
    QMutexLocker locker(m_mutex);
    m_port->setPortName(m_setting.name);
    if (m_port->open(m_setting.openMode))
    {
        m_port->setBaudRate(m_setting.baudRate);
        m_port->setDataBits(m_setting.dataBits);
        m_port->setStopBits(m_setting.stopBits);
        m_port->setParity(m_setting.parity);
        m_port->setFlowControl(m_setting.flowContrl);
        connect(m_port, &QSerialPort::readyRead, this, &SerialPort::onRead);
        connect(m_port, &QSerialPort::errorOccurred, this, &SerialPort::onError);

        m_timer->start();
        return true;
    }
    return false;
}

bool SerialPort::isOpen(void)
{
    QMutexLocker locker(m_mutex);
    return m_port->isOpen();
}

// windows跨线程关闭串口会闪退
// 跨线程请调用closeSync()
bool SerialPort::close(void)
{
    QMutexLocker locker(m_mutex);
    if (m_port->isOpen())
    {
        m_port->disconnect();
        m_port->close();

        m_timer->stop();

        if (m_port->isOpen())
            return false;
    }
    return true;
}

// 跨线程请调用writeSync()
qint32 SerialPort::write(const QByteArray &ba)
{
    QMutexLocker locker(m_mutex);
    if (m_port->isOpen())
    {
        m_statistics->sendBytes(ba.size());
        return static_cast<qint32>(m_port->write(ba));
    }
    return 0;
}

// 跨线程请调用readSync()
qint32 SerialPort::read(QByteArray &ba)
{
    QMutexLocker locker(m_mutex);
    if (m_port->isOpen())
    {
        ba = m_port->readAll();
        m_statistics->receiveBytes(ba.size());
        return ba.size();
    }
    return 0;
}

QString SerialPort::errorString() const
{
    QMutexLocker locker(m_mutex);
    return m_port->errorString();
}

void SerialPort::clearError()
{
    QMutexLocker locker(m_mutex);
    m_port->clearError();
}

bool SerialPort::openSync(int timeout_ms)
{
    QMutexLocker locker(m_mutex);
    m_port->setPortName(m_setting.name);
    m_syncFlag = false;
    QTimer::singleShot(0, this, [=](){
        if (m_port->open(m_setting.openMode))
        {
            m_port->setBaudRate(m_setting.baudRate);
            m_port->setDataBits(m_setting.dataBits);
            m_port->setStopBits(m_setting.stopBits);
            m_port->setParity(m_setting.parity);
            m_port->setFlowControl(m_setting.flowContrl);
            connect(m_port, &QSerialPort::readyRead, this, &SerialPort::onRead);
            connect(m_port, &QSerialPort::errorOccurred, this, &SerialPort::onError);

            m_timer->start();
        }
        m_syncFlag = true;
        return true;
    });
    return waitForFinished(timeout_ms);
}

bool SerialPort::closeSync(int timeout_ms)
{
    QMutexLocker locker(m_mutex);
    m_syncFlag = false;
    QTimer::singleShot(0, this, [=](){
        if (m_port->isOpen())
        {
            m_port->disconnect();
            m_port->close();

            m_timer->stop();

            if (m_port->isOpen())
            {
                m_syncFlag = true;
                return false;
            }
        }
        m_syncFlag = true;
        return true;
    });
    return waitForFinished(timeout_ms);
}

qint32 SerialPort::writeSync(const QByteArray &ba, int timeout_ms)
{
    QMutexLocker locker(m_mutex);
    m_syncFlag = false;
    qint32 bytes = 0;
    QTimer::singleShot(0, this, [&](){
        if (m_port->isOpen())
        {
            m_statistics->sendBytes(ba.size());
            bytes = static_cast<qint32>(m_port->write(ba));
        }
        m_syncFlag = true;
    });
    waitForFinished(timeout_ms);
    return bytes;
}

qint32 SerialPort::readSync(QByteArray &ba, int timeout_ms)
{
    QMutexLocker locker(m_mutex);
    m_syncFlag = false;
    qint32 bytes = 0;
    QTimer::singleShot(0, this, [&](){
        if (m_port->isOpen())
        {
            ba = m_port->readAll();
            m_statistics->receiveBytes(ba.size());
            bytes = ba.size();
        }
        m_syncFlag = true;
    });
    waitForFinished(timeout_ms);
    return bytes;
}

void SerialPort::onError(QSerialPort::SerialPortError error)
{
    QMutexLocker locker(m_mutex);
    if(QSerialPort::ResourceError == error)
    {
        if(m_port->isOpen())
        {
//            emit ui->pbOpen->clicked();
//            MainWindow::getInstance()->slotStatusShowMessage("Disconnected", 5);
            qDebug() << "SerialPort is Error : " << error;
        }
    }
}

void SerialPort::onRead(void)
{
    QMutexLocker locker(m_mutex);
    QByteArray buf;
    buf = m_port->readAll();
    if (!buf.isEmpty())
    {
        m_statistics->receiveBytes(buf.size());

        // 对外发送数据，因为可能要看最原始的数据
        emit readCompleted(buf);

        if (m_finder)
        {
            // 寻找合法包
            QList<ProtocolPacket> packets = m_finder->find(buf);
            if (!packets.isEmpty())
            {
                emit receivePackets(packets);

#if 0
                qDebug() << "timestamp, buffer = ";
                foreach (auto p, packets)
                {
                    qDebug() << packets.at(0).timestamp << packets.at(0).buffer;
                }
#endif
            }
        }
//        qDebug() << "buf = " << buf;
    }
}

QSerialPort::StopBits SerialPort::toStopBit(const QString &bit)
{
    if (bit == tr("1"))
        return QSerialPort::OneStop;
    else if (bit == tr("1.5"))
        return QSerialPort::OneAndHalfStop;
    else if (bit == tr("2"))
        return QSerialPort::TwoStop;
    return QSerialPort::OneStop;
}

QSerialPort::DataBits SerialPort::toDataBit(const QString &bit)
{
    if (bit == tr("8"))
        return QSerialPort::Data8;

    if (bit == tr("7"))
        return QSerialPort::Data7;

    if (bit == tr("6"))
        return QSerialPort::Data6;

    if (bit == tr("5"))
        return QSerialPort::Data5;
    return QSerialPort::Data8;
}

QSerialPort::Parity SerialPort::toParityBit(const QString &bit)
{
    if (bit == tr("None"))
        return QSerialPort::NoParity;
    if (bit == tr("Odd"))
        return QSerialPort::OddParity;
    if (bit == tr("Even"))
        return QSerialPort::EvenParity;
    return QSerialPort::NoParity;
}

QSerialPort::FlowControl SerialPort::toFlowControl(const QString &bit)
{
    if (bit == tr("None"))
        return QSerialPort::NoFlowControl;
    if (bit == tr("XON/XOFF"))
        return QSerialPort::SoftwareControl;
    if (bit == tr("RTS/CTS"))
        return QSerialPort::HardwareControl;
    return QSerialPort::NoFlowControl;
}

QString SerialPort::fromStopBit(QSerialPort::StopBits bit)
{
    QString str = "";
    switch (bit)
    {
        case QSerialPort::StopBits::OneStop:
            str = "1";
        break;
        case QSerialPort::StopBits::OneAndHalfStop:
            str = "1.5";
        break;
        case QSerialPort::StopBits::TwoStop:
            str = "2";
        break;
        default:
        break;
    }
    return str;
}

QString SerialPort::fromDataBit(QSerialPort::DataBits bit)
{
    QString str = "";
    switch (bit)
    {
        case QSerialPort::DataBits::Data5:
            str = "5";
        break;
        case QSerialPort::DataBits::Data6:
            str = "6";
        break;
        case QSerialPort::DataBits::Data7:
            str = "7";
        break;
        case QSerialPort::DataBits::Data8:
            str = "8";
        break;
        default:
        break;
    }
    return str;
}

QString SerialPort::fromParityBit(QSerialPort::Parity bit)
{
    QString str = "";
    switch (bit)
    {
        case QSerialPort::Parity::NoParity:
            str = "None";
        break;
        case QSerialPort::Parity::EvenParity:
            str = "Even";
        break;
        case QSerialPort::Parity::OddParity:
            str = "Odd";
        break;
        default:
        break;
    }
    return str;
}

QString SerialPort::fromFlowControl(QSerialPort::FlowControl bit)
{
    QString str = "";
    switch (bit)
    {
        case QSerialPort::FlowControl::NoFlowControl:
            str = "None";
        break;
        case QSerialPort::FlowControl::HardwareControl:
            str = "RTS/CTS";
        break;
        case QSerialPort::FlowControl::SoftwareControl:
            str = "XON/XOFF";
        break;
        default:
        break;
    }
    return str;
}
